jetcrab\bytecode\expressions/
assignment.rs

1use crate::ast::Node;
2use crate::vm::instructions::Instruction;
3use crate::vm::types::{CodeAddress, LocalIndex};
4
5pub trait AssignmentGenerator {
6    fn generate_assignment_expression(&mut self, node: &Node);
7    fn generate_conditional_expression(&mut self, node: &Node);
8    fn generate_call_expression(&mut self, node: &Node);
9    fn generate_new_expression(&mut self, node: &Node);
10    fn generate_member_expression(&mut self, node: &Node);
11}
12
13pub trait AssignmentCore {
14    fn instructions(&mut self) -> &mut Vec<Instruction>;
15    fn visit_node(&mut self, node: &Node);
16}
17
18impl<T> AssignmentGenerator for T
19where
20    T: AssignmentCore + crate::bytecode::scope::constants::ConstantCore + crate::bytecode::scope::local_vars::ScopeManager,
21{
22    fn generate_assignment_expression(&mut self, node: &Node) {
23        if let Node::AssignmentExpression(expr) = node {
24            match expr.operator.as_str() {
25                "=" => {
26                    // Simple assignment
27                    match &*expr.left {
28                        Node::Identifier(ident) => {
29                            // Variable assignment: x = value
30                            self.visit_node(&expr.right);
31                            let local_idx = self.get_or_create_local(ident);
32                            self.instructions().push(Instruction::StoreLocal(local_idx));
33                        }
34                        Node::MemberExpression(member) => {
35                            // Property assignment: obj.prop = value or obj[prop] = value
36                            self.visit_node(&member.object);  // Push object
37                            
38                            // Handle property names correctly
39                            match &*member.property {
40                                Node::Identifier(name) if !member.computed => {
41                                    // obj.prop = value (dot notation)
42                                    let constant_id = <Self as crate::bytecode::scope::constants::ConstantManager>::add_constant(self, name.clone());
43                                    self.instructions().push(Instruction::PushConst(constant_id));
44                                }
45                                _ => {
46                                    // obj[prop] = value (bracket notation)
47                                    self.visit_node(&member.property);
48                                }
49                            }
50                            
51                            self.visit_node(&expr.right);  // Push value
52                            self.instructions().push(Instruction::SetPropertyAssign);
53                        }
54                        _ => {
55                            // Fallback for other types
56                            self.visit_node(&expr.right);
57                            self.instructions().push(Instruction::StoreLocal(LocalIndex::new(0)));
58                        }
59                    }
60                }
61                "+=" => {
62                    // Compound assignment: a += b is equivalent to a = a + b
63                    if let Node::Identifier(ident) = &*expr.left {
64                        let local_idx = self.get_or_create_local(ident);
65                        
66                        // Load the current value of the variable
67                        self.instructions().push(Instruction::LoadLocal(local_idx));
68                        
69                        // Generate the right-hand side expression
70                        self.visit_node(&expr.right);
71                        
72                        // Add the values
73                        self.instructions().push(Instruction::Add);
74                        
75                        // Store the result back
76                        self.instructions().push(Instruction::StoreLocal(local_idx));
77                    } else {
78                        self.instructions().push(Instruction::PushUndefined);
79                    }
80                }
81                "-=" => {
82                    // Compound assignment: a -= b is equivalent to a = a - b
83                    if let Node::Identifier(ident) = &*expr.left {
84                        let local_idx = self.get_or_create_local(ident);
85                        
86                        // Load the current value of the variable
87                        self.instructions().push(Instruction::LoadLocal(local_idx));
88                        
89                        // Generate the right-hand side expression
90                        self.visit_node(&expr.right);
91                        
92                        // Subtract the values
93                        self.instructions().push(Instruction::Sub);
94                        
95                        // Store the result back
96                        self.instructions().push(Instruction::StoreLocal(local_idx));
97                    } else {
98                        self.instructions().push(Instruction::PushUndefined);
99                    }
100                }
101                "*=" => {
102                    // Compound assignment: a *= b is equivalent to a = a * b
103                    if let Node::Identifier(ident) = &*expr.left {
104                        let local_idx = self.get_or_create_local(ident);
105                        
106                        // Load the current value of the variable
107                        self.instructions().push(Instruction::LoadLocal(local_idx));
108                        
109                        // Generate the right-hand side expression
110                        self.visit_node(&expr.right);
111                        
112                        // Multiply the values
113                        self.instructions().push(Instruction::Mul);
114                        
115                        // Store the result back
116                        self.instructions().push(Instruction::StoreLocal(local_idx));
117                    } else {
118                        self.instructions().push(Instruction::PushUndefined);
119                    }
120                }
121                "/=" => {
122                    // Compound assignment: a /= b is equivalent to a = a / b
123                    if let Node::Identifier(ident) = &*expr.left {
124                        let local_idx = self.get_or_create_local(ident);
125                        
126                        // Load the current value of the variable
127                        self.instructions().push(Instruction::LoadLocal(local_idx));
128                        
129                        // Generate the right-hand side expression
130                        self.visit_node(&expr.right);
131                        
132                        // Divide the values
133                        self.instructions().push(Instruction::Div);
134                        
135                        // Store the result back
136                        self.instructions().push(Instruction::StoreLocal(local_idx));
137                    } else {
138                        self.instructions().push(Instruction::PushUndefined);
139                    }
140                }
141                _ => {
142                    // Unsupported operator, fallback to simple assignment
143                    self.visit_node(&expr.right);
144                    
145                    if let Node::Identifier(ident) = &*expr.left {
146                        let local_idx = self.get_or_create_local(ident);
147                        self.instructions().push(Instruction::StoreLocal(local_idx));
148                    } else {
149                        self.instructions().push(Instruction::StoreLocal(LocalIndex::new(0)));
150                    }
151                }
152            }
153        }
154    }
155
156    fn generate_conditional_expression(&mut self, node: &Node) {
157        if let Node::ConditionalExpression(expr) = node {
158            self.visit_node(&expr.test);
159
160            let jump_to_alternate = self.instructions().len();
161            self.instructions()
162                .push(Instruction::JumpIfFalse(CodeAddress::new(0)));
163
164            self.visit_node(&expr.consequent);
165
166            let jump_to_end = self.instructions().len();
167            self.instructions()
168                .push(Instruction::Jump(CodeAddress::new(0)));
169
170            let alternate_start = self.instructions().len();
171            self.instructions()[jump_to_alternate] =
172                Instruction::JumpIfFalse(CodeAddress::new(alternate_start));
173
174            self.visit_node(&expr.alternate);
175
176            let end_pos = self.instructions().len();
177            self.instructions()[jump_to_end] = Instruction::Jump(CodeAddress::new(end_pos));
178        }
179    }
180
181    fn generate_call_expression(&mut self, node: &Node) {
182        if let Node::CallExpression(expr) = node {
183            // Check if this is a built-in function call
184            if let Node::MemberExpression(member) = &*expr.callee {
185                if let (Node::Identifier(obj_name), Node::Identifier(prop_name)) = 
186                    (&*member.object, &*member.property) {
187                    
188                    // Check for Math functions
189                    if obj_name == "Math" {
190                        let builtin_name = format!("Math.{}", prop_name);
191                        // Push arguments first
192                        for arg in &expr.arguments {
193                            self.visit_node(arg);
194                        }
195                        // Call built-in
196                        self.instructions().push(Instruction::CallBuiltin(
197                            builtin_name,
198                            crate::vm::types::ArgIndex::new(expr.arguments.len())
199                        ));
200                        return;
201                    }
202                    
203                    // Check for String prototype methods
204                    if let Node::String(_) = &*member.object {
205                        let builtin_name = format!("String.prototype.{}", prop_name);
206                        // Push the string first, then arguments
207                        self.visit_node(&*member.object);
208                        for arg in &expr.arguments {
209                            self.visit_node(arg);
210                        }
211                        // Call built-in
212                        self.instructions().push(Instruction::CallBuiltin(
213                            builtin_name,
214                            crate::vm::types::ArgIndex::new(expr.arguments.len() + 1)
215                        ));
216                        return;
217                    }
218                    
219                    // Check for Array prototype methods
220                    if let Node::Identifier(obj_name) = &*member.object {
221                        if self.is_array_variable(obj_name) && (prop_name == "push" || prop_name == "pop") {
222                            let builtin_name = format!("Array.prototype.{}", prop_name);
223                            // Push the array first, then arguments
224                            self.visit_node(&*member.object);
225                            for arg in &expr.arguments {
226                                self.visit_node(arg);
227                            }
228                            // Call built-in
229                            self.instructions().push(Instruction::CallBuiltin(
230                                builtin_name,
231                                crate::vm::types::ArgIndex::new(expr.arguments.len() + 1)
232                            ));
233                            return;
234                        }
235                    }
236                }
237            }
238            
239            // Default call expression handling
240            for arg in &expr.arguments {
241                self.visit_node(arg);
242            }
243            self.visit_node(&expr.callee);
244            self.instructions()
245                .push(Instruction::Call(crate::vm::types::FunctionIndex::new(
246                    expr.arguments.len(),
247                )));
248        }
249    }
250
251    fn generate_new_expression(&mut self, node: &Node) {
252        if let Node::NewExpression(expr) = node {
253            for arg in &expr.arguments {
254                self.visit_node(arg);
255            }
256            self.visit_node(&expr.callee);
257            self.instructions().push(Instruction::New);
258        }
259    }
260
261    fn generate_member_expression(&mut self, node: &Node) {
262        if let Node::MemberExpression(expr) = node {
263            self.visit_node(&expr.object);
264            
265            // Handle property names correctly
266            match &*expr.property {
267                Node::Identifier(name) => {
268                    // Push the string value directly for property names
269                    let constant_id = <Self as crate::bytecode::scope::constants::ConstantManager>::add_constant(self, name.clone());
270                    self.instructions().push(Instruction::PushConst(constant_id));
271                }
272                _ => {
273                    // For other property types, visit normally
274                    self.visit_node(&expr.property);
275                }
276            }
277            
278            self.instructions().push(Instruction::GetProperty);
279        }
280    }
281}